/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.coreGenerator.base.entitygen;

import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.property.ClassPropertyGenerator;
import com.inspur.edp.cef.core.data.EntityDataBase;
import com.inspur.edp.cef.core.data.ValueObjDataBase;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.EnumIndexType;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.common.MediatePropertyInfo;
import com.inspur.edp.cef.generator.coreGenerator.base.accessorgen.method.GetChildsGenerator;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.field.ChildFieldDefaultValueGenerator;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.field.ChildFieldGenerator;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.field.PropNamesFieldGenerator;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.method.*;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.property.*;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import org.eclipse.jdt.core.dom.Modifier;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public abstract class EntityDataImplGenerator extends DataImplGenerator {

  protected EntityDataImplGenerator(EntityGeneratorContext parContext, IGspCommonObject node,
      MediateType targetInterfaceType) {
    super(parContext, node, targetInterfaceType);
  }

  protected EntityDataImplGenerator(EntityGeneratorContext parContext, String node,
      MediateType targetInterfaceType) {
    super(parContext, node, targetInterfaceType);
  }

  @Override
  protected void beforeInitialize() {
    super.beforeInitialize();
    ParentContext.getProjContext().getEntityDataMediateTypes()
        .put(NodeCode, new MediateType("", getName()));
  }

  protected boolean isValueObject() {
    return false;
  }

  @Override
  protected ArrayList<Modifier.ModifierKeyword> getAccessModifier() {
    ArrayList<Modifier.ModifierKeyword> list = new ArrayList<Modifier.ModifierKeyword>();
    list.add(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
    return list;
  }

  @Override
  protected ArrayList<TypeInfo> addParentInterfaces() {
    ArrayList<TypeInfo> list = new ArrayList<TypeInfo>();
    list.add(new TypeInfo(Cloneable.class));
    list.add(new TypeInfo(TargetInterfaceType.getFullName()));
    addExtendParentInterfaces(list);
    return list;
  }

  protected void addExtendParentInterfaces(ArrayList<TypeInfo> parents) {
  }

  @Override
  public String getName() {
    String code = ParentContext.getProjContext().getCefGenContext().getMetadata().getHeader()
        .getCode();
    if (NodeCode==null||NodeCode.equals("")|| NodeCode.equals("RootNode")) {
      return code + "Data";
    }
    return code + "_" + NodeCode + "Data";
  }

  @Override
  protected TypeInfo setParentClass() {
    return isValueObject() ? new TypeInfo(ValueObjDataBase.class.getTypeName())
            : new TypeInfo(EntityDataBase.class.getTypeName());
  }

  @Override
  protected void addExtendPropertyGenerator(ArrayList<ClassPropertyGenerator> proGenerators) {
    super.addExtendPropertyGenerator(proGenerators);
    if (Node != null && Node instanceof IGspCommonObject
        && ((IGspCommonObject) Node).getParentObject() != null
        && ParentIdPropGenerator.NeedGenerate((IGspCommonObject) Node)) {
      ParentIdPropGenerator parentIdGen = new ParentIdPropGenerator(TargetInterfaceType,
          (IGspCommonObject) Node);
      proGenerators.add(parentIdGen);
    }
    if (Node instanceof SimpleDataTypeDef) {
      proGenerators.add(new SimpleUdtDataValuePropGenerator((SimpleDataTypeDef) Node));
    }
  }

  @Override
  protected ChildPropGenerator getChildPropGenerator(String childCode,
      MediatePropertyInfo propertyInfo, String privatePropName) {
    return new ChildPropGenerator(ParentContext, childCode, propertyInfo, privatePropName);
  }

  @Override
  protected List<BaseTypePropGenerator> getBaseTypePropGenerators(
      MediatePropertyInfo propertyInfo) {
    List<BaseTypePropGenerator> baseTypePropGenerators = new ArrayList<>();

    IGspCommonField element = propertyInfo.getElement();
    if (element.getObjectType() == GspElementObjectType.Enum && !element.getIsUdt()) {
      GspEnumValue defaultEnumValue = element.getContainEnumValues().stream()
          .filter(item -> item.getIsDefaultEnum()).findFirst().orElse(element.getContainEnumValues().get(0));

      if (element.getEnumIndexType() == EnumIndexType.Integer) {
        String privatePropName = "_" + propertyInfo.getPropertyName().toLowerCase();
        EnumChildFieldGenerator privatePropGen = new EnumChildFieldGenerator(privatePropName,
            propertyInfo.getReturnType(), defaultEnumValue.getIndex(), isDefaultNull(element));
        FieldGenerators.add(privatePropGen);
        EnumTypeProGenerator baseTypeGen = new EnumTypeProGenerator(propertyInfo, privatePropName);
        baseTypePropGenerators.add(baseTypeGen);
        if (Character.isLowerCase(propertyInfo.getPropertyName().charAt(0)) && !propertyInfo.getExtendInfos().containsKey("AssoUdtAtrribute")) {
          baseTypePropGenerators.add(new EnumTypeProGenerator(propertyInfo, privatePropName, true));
        }
        return baseTypePropGenerators;
      } else if (element.getEnumIndexType() == EnumIndexType.String) {
        String privatePropName = "_" + propertyInfo.getPropertyName().toLowerCase();
        EnumChildFieldStrGenerator privatePropGenStr = new EnumChildFieldStrGenerator(
            privatePropName,
            propertyInfo.getReturnType(), defaultEnumValue.getStringIndex(),isDefaultNull(element));
        this.FieldGenerators.add(privatePropGenStr);
        baseTypePropGenerators.add(new EnumTypeProGenerator(propertyInfo, privatePropName));
        if (Character.isLowerCase(propertyInfo.getPropertyName().charAt(0))) {
          baseTypePropGenerators.add(new EnumTypeProGenerator(propertyInfo, privatePropName, true));
        }
        return baseTypePropGenerators;
      }
    }

    //AbstractPropertyInfo pInfo = getDatePropertyInfos(propertyInfo.getPropertyName());
    if (element.getMDataType() == GspElementDataType.Date) {

      String privateDateFieldName = propertyInfo.getPropertyName().toLowerCase() + "field";
      ChildFieldGenerator privateDatePropGen = new ChildFieldGenerator(privateDateFieldName,
          propertyInfo.getReturnType());
      FieldGenerators.add(privateDatePropGen);
      DatePropGenerator dateGen = getDatePropGenerator(propertyInfo, privateDateFieldName);
      baseTypePropGenerators.add(dateGen);
      if (Character.isLowerCase(propertyInfo.getPropertyName().charAt(0))) {
        baseTypePropGenerators.add(getDatePropGenerator(propertyInfo, privateDateFieldName, true));
      }
      return baseTypePropGenerators;
    } else if (element.getMDataType() == GspElementDataType.DateTime) {
      String privateDateFieldName = propertyInfo.getPropertyName().toLowerCase() + "field";
      ChildFieldGenerator privateDatePropGen = new ChildFieldGenerator(privateDateFieldName,
          propertyInfo.getReturnType());
      FieldGenerators.add(privateDatePropGen);
      DateTimePropGenerator dateTimePropGenerator = getDateTimePropGenerator(propertyInfo,
          privateDateFieldName);
      baseTypePropGenerators.add(dateTimePropGenerator);
      if (Character.isLowerCase(propertyInfo.getPropertyName().charAt(0))) {
        baseTypePropGenerators
            .add(getDateTimePropGenerator(propertyInfo, privateDateFieldName, true));
      }
      return baseTypePropGenerators;
    } else {
      String privateDateFieldName = propertyInfo.getPropertyName().toLowerCase() + "field";
      if (element != null && isDefaultNull(element)) {
        ChildFieldGenerator privateDatePropGen = new ChildFieldGenerator(privateDateFieldName,
            propertyInfo.getReturnType());
        FieldGenerators.add(privateDatePropGen);
      } else {
        ChildFieldDefaultValueGenerator childFieldDefaultValueGenerator = new ChildFieldDefaultValueGenerator(
            privateDateFieldName, propertyInfo, ParentContext);
        FieldGenerators.add(childFieldDefaultValueGenerator);
      }
      baseTypePropGenerators.add(new BaseTypePropGenerator(propertyInfo, privateDateFieldName));
      if (Character.isLowerCase(propertyInfo.getPropertyName().charAt(0)) && !propertyInfo.getExtendInfos().containsKey("AssoUdtAtrribute")) {
        baseTypePropGenerators
            .add(new BaseTypePropGenerator(propertyInfo, privateDateFieldName, true));
      }
      return baseTypePropGenerators;
    }
  }

  private static boolean isDefaultNull(IGspCommonField element) {
    return element != null && element instanceof GspBizEntityElement
        && ((GspBizEntityElement) element).getIsDefaultNull();
  }

  ///#endregion
  protected DatePropGenerator getDatePropGenerator(MediatePropertyInfo propertyInfo,
      String fieldName) {
    return new DatePropGenerator(ParentContext, NodeCode, propertyInfo, fieldName);
  }

  protected DatePropGenerator getDatePropGenerator(MediatePropertyInfo propertyInfo,
      String fieldName,
      boolean convertToUpperCase) {
    return new DatePropGenerator(ParentContext, NodeCode, propertyInfo, fieldName,
        convertToUpperCase);
  }

  private DateTimePropGenerator getDateTimePropGenerator(MediatePropertyInfo propertyInfo,
      String fieldName) {
    return new DateTimePropGenerator(propertyInfo, fieldName);
  }

  private DateTimePropGenerator getDateTimePropGenerator(MediatePropertyInfo propertyInfo,
      String fieldName, boolean convertToUpperCase) {
    return new DateTimePropGenerator(propertyInfo, fieldName, convertToUpperCase);
  }


  //#region ExtendClass
  protected abstract EntityDataImplGenerator getDataChildsGenerator(
      EntityGeneratorContext parContext, IGspCommonObject node);

  @Override
  protected BaseDataGenerator getChildsGenerator(EntityGeneratorContext parContext,
      IGspCommonObject node) {
    return getDataChildsGenerator(parContext, node);
  }
  //#endregion

  //region Method
  @Override
  protected ArrayList<ClassMethodGenerator> createMethodGenerators() {

    ArrayList<ClassMethodGenerator> methods = super.createMethodGenerators();
    if (methods == null) {
      methods = new java.util.ArrayList<ClassMethodGenerator>();
    }
    methods.add(new CopySelfGenerator());
    methods.add(new DataStaticGetValueGenerator(TargetInterfaceType,PropertyInfos));
    methods.add(new DataGetValueGenerator(ParentContext,NodeCode));
    methods.add(new DataStaticSetValueGenerator(PropertyInfos,ParentContext,TargetInterfaceType));
    methods.add(new SetValueGenerator(ParentContext,NodeCode));
    if (isHasCase()) {
      methods.add(new CreateValueGenerator(ParentContext, PropertyInfos, NodeCode));
    }
      //FieldGenerators.add(new PropNamesFieldGenerator(PropertyInfos, "propertyNames"));
      String dataClassName = ParentContext.getDataImplTypes().get(NodeCode).getClassName();
      if (dataClassName.endsWith("Data")) {
          methods.add(new GetDataPropNamesGenerator(ParentContext, dataClassName));
      } else {
          FieldGenerators.add(new PropNamesFieldGenerator(PropertyInfos, "propertyNames"));
          methods.add(new GetPropNamesGenerator(PropertyInfos, "propertyNames"));
      }
    //var methods = new List<ClassMethodGenerator>() { copySelfGen, getValueGen, getPropNamesGen, setValueGen };
    if (!isValueObject()) {
      GetChildsGenerator getChildGen = getChildsMedGenerator(PropertyInfos);
      methods.add(getChildGen);

      if (!ChildTypes.isEmpty()) {
        DataCreateChildGenerator createChildGen = new DataCreateChildGenerator(ChildTypes);
        methods.add(createChildGen);
        methods.add(new MergeChildDataGenerator(TargetInterfaceType, ChildTypes));
      }
    }

    addExtendMethodGenerators(methods);
    return methods;
  }

  private GetChildsGenerator getChildsMedGenerator(List<MediatePropertyInfo> propertyInfos) {
    List<Modifier.ModifierKeyword> access = Arrays.asList(Modifier.ModifierKeyword.PROTECTED_KEYWORD);
    return new GetChildsGenerator(Node, access);
  }

  protected void addExtendMethodGenerators(java.util.ArrayList<ClassMethodGenerator> methodGens) {

  }

  //endregion

  @Override
  protected DynamicPropSetPropertyGenerator getDynamicPropSetPropertyGenerator(
      MediatePropertyInfo info) {
    return new DynamicPropSetPropertyGenerator(info);
  }

  private boolean isHasCase() {
    List<MediatePropertyInfo> propertyInfos = PropertyInfos.stream().filter(
            item -> item.getElement() != null
                    && item.getElement().getObjectType() == GspElementObjectType.Association)
            .collect(Collectors.toList());
    return !propertyInfos.isEmpty();
  }
}
